//初始化WS服务

package websocket

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/zeromicro/go-zero/core/logx"
	"net/http"
	"sync"
)

// 定义服务的基础功能
type Server struct {
	opt            *serverOption
	authentication Authentication

	addr    string
	pattern string
	upgrade websocket.Upgrader
	logx.Logger
	routes map[string]HandlerFunc

	//存储连接对象
	sync.RWMutex
	connToUser map[*Conn]string
	userToConn map[string]*Conn
}

// 根据传入的地址创建服务
func NewServer(addr string, opts ...ServerOptions) *Server {
	opt := newServerOptions(opts...)

	return &Server{
		addr:       addr,
		pattern:    opt.pattern,
		upgrade:    websocket.Upgrader{},
		Logger:     logx.WithContext(context.Background()),
		routes:     make(map[string]HandlerFunc),
		connToUser: make(map[*Conn]string),
		userToConn: make(map[string]*Conn),
		//authentication: new(authentication),  ---->默认的
		authentication: opt.Authentication,
		opt:            &opt,
	}
}

// 服务实际执行的方法
func (s *Server) ServerWs(w http.ResponseWriter, r *http.Request) {
	//处理运行过程中可能抛出的系统性的panic，将其恢复
	defer func() {
		if err := recover(); err != nil {
			s.Errorf("[IN SERVER.GO] server handler ws recover err &v", err)
		}
	}()

	//获取连接对象
	conn := NewConn(s, w, r)
	if conn == nil {
		return
	}
	//conn, err := s.upgrade.Upgrade(w, r, nil)
	//if err != nil {
	//	s.Errorf("[IN SERVER.GO] server handler ws upgrade err &v", err)
	//	return
	//}

	//鉴权
	if !s.authentication.Auth(w, r) {
		//conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("[IN SERVER.GO] 没有权限")))
		s.Send(&Message{FrameType: FrameData, Data: fmt.Sprintf("[IN SERVER.GO] 没有权限")}, conn)
		s.Close(conn)
		return
	}

	//记录连接
	s.addConn(conn, r)

	//处理连接
	go s.handlerConn(conn)
}

// 根据连接对象进行任务处理
func (s *Server) handlerConn(conn *Conn) {

	uids := s.GetUsers(conn)
	conn.Uid = uids[0]

	for {
		//获取请求消息
		_, msg, err := conn.ReadMessage()
		if err != nil {
			s.Errorf("[IN SERVER.GO] websocket conn read message err %v", err)
			s.Close(conn)
			return
		}

		//解析消息
		var message Message
		err = json.Unmarshal(msg, &message)
		if err != nil {
			s.Errorf("[IN SERVER.GO] json unmarshal err &v , msg %v", err, string(msg))
			s.Close(conn)
			return
		}

		//todo: 给客户端回复一个ack

		//根据不同消息（心跳、普通消息）类型进行不同处理
		switch message.FrameType {
		case FramePing:
			s.Send(&Message{FrameType: FramePing}, conn)

		case FrameData:
			//根据请求中的method分发路由并执行
			if handler, ok := s.routes[message.Method]; ok {
				handler(s, conn, &message)
			} else {
				s.Send(&Message{FrameType: FrameData, Data: fmt.Sprintf("[IN SERVER.GO] 不存在可以执行的方法 %v", message.Method)}, conn)
				//conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("[IN SERVER.GO] 不存在可以执行的方法 %v", message.Method)))
			}
		}
	}
}

// 读取消息的ack
func (s *Server) readAck() {

}

// 任务的处理
func (s *Server) handlerWrite() {

}

// 存储连接对象
func (s *Server) addConn(conn *Conn, req *http.Request) {
	//获取用户id
	uid := s.authentication.UserId(req)

	s.RWMutex.Lock()
	defer s.RWMutex.Unlock()

	//禁止重复登录
	if c := s.userToConn[uid]; c != nil {
		//关闭之前的连接
		s.Close(c)
	}

	s.connToUser[conn] = uid
	s.userToConn[uid] = conn
}

// 根据连接获取用户及根据用户获取连接
func (s *Server) GetConn(uid string) *Conn {
	s.RWMutex.RLock()
	defer s.RWMutex.RUnlock()

	return s.userToConn[uid]
}
func (s *Server) GetConns(uids ...string) []*Conn {
	if len(uids) == 0 {
		return nil
	}

	s.RWMutex.RLock()
	defer s.RWMutex.RUnlock()

	res := make([]*Conn, 0, len(uids))
	for _, uid := range uids {
		res = append(res, s.userToConn[uid])
	}

	return res
}
func (s *Server) GetUsers(conns ...*Conn) []string {
	s.RWMutex.RLock()
	defer s.RWMutex.RUnlock()

	var res []string
	if len(conns) == 0 {
		//获取全部
		res = make([]string, 0, len(s.connToUser))
		for _, uid := range s.connToUser {
			res = append(res, uid)
		}
	} else {
		//获取部分
		res = make([]string, 0, len(conns))
		for _, conn := range conns {
			res = append(res, s.connToUser[conn])
		}
	}

	return res
}

// 关闭连接
func (s *Server) Close(conn *Conn) {
	s.RWMutex.Lock()
	defer s.RWMutex.Unlock()

	uid := s.connToUser[conn]
	//如果已经被关闭了
	if uid == "" {
		return
	}

	delete(s.connToUser, conn)
	delete(s.userToConn, uid)

	conn.Close()
}

// 发送消息
func (s *Server) SendByUserId(msg interface{}, sendIds ...string) error {
	if len(sendIds) == 0 {
		return nil
	}

	return s.Send(msg, s.GetConns(sendIds...)...)
}
func (s *Server) Send(msg interface{}, conns ...*Conn) error {
	if len(conns) == 0 {
		return nil
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	for _, conn := range conns {
		err = conn.WriteMessage(websocket.TextMessage, data)
		if err != nil {
			return err
		}
	}
	return nil
}

// 给服务添加路由的方法
func (s *Server) AddRoutes(rs []Route) {
	for _, r := range rs {
		s.routes[r.Method] = r.Handler
	}
}

// 服务启动的方法
func (s *Server) Start() {
	http.HandleFunc(s.pattern, s.ServerWs)
	fmt.Println("启动websocket服务")
	s.Info(http.ListenAndServe(s.addr, nil))
}

// 停止服务
func (s *Server) Stop() {
	fmt.Println("停止服务")
}
